/* 
 * 
 * Copyright (c) 2007 John Wells
 * Copyright (c) 2010 Photonic Games

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Graphics;

//using GameLevel.ContentPipeline.Properties;

namespace GameLevel.ContentPipeline.Leadwerks14
{
    /// <summary>An object representation of the .3dw file created by the Leadwerks 3D World Builder (v5.52) software</summary>
    public class Leadwerks14Content : ContentItem
    {
        #region Fields
        // Constants
        private const ushort ExpectedVersion = 14;

        // Public fields
        public MapFlags Flags;
        public List<Brush> Brushes = new List<Brush>();
        public List<Entity> Entities = new List<Entity>();
        public List<Group> Groups = new List<Group>();
        public List<Terrain> Terrains = new List<Terrain>();
        public List<Material> Materials = new List<Material>();
        public List<VisibilityGroup> VisibilityGroups = new List<VisibilityGroup>();
        public List<Texture2DContent> Lightmaps = new List<Texture2DContent>();
        public List<MeshReference> MeshReferences = new List<MeshReference>();
        public List<Mesh> Meshes = new List<Mesh>();
        public int[] indexMap;

        // Enumerations
        [Flags]
        public enum MapFlags
        {
            None = 0
        }
        #endregion

        #region Methods
        /// <summary>Read a Header object from a pre-seek'd BinaryReader</summary>
        public static Leadwerks14Content Read(BinaryReader reader, ContentImporterContext context)
        {
            // Read the version number
            ushort version = reader.ReadUInt16();
            if (version != Leadwerks14Content.ExpectedVersion)
                throw new InvalidContentException(string.Format("Expected header version {0} but found {0}", Leadwerks14Content.ExpectedVersion, version));

            Leadwerks14Content output = new Leadwerks14Content();

            // Read the map flags
            output.Flags = (MapFlags)reader.ReadByte();

            // The next two ints are the count of names and the offset to the start of them
            string[] names = new string[reader.ReadInt32()];
            int nameTableOffset = reader.ReadInt32();

            // The next two ints are the count of objects and the offset to the start of them
            int objectCount = reader.ReadInt32();
            int objectTableOffset = reader.ReadInt32();

            // Read the name table
            reader.BaseStream.Seek(nameTableOffset, SeekOrigin.Begin);
            if (names.Length > 0)
                for (int index = 0; index < names.Length; index++)
                {
                    names[index] = Leadwerks14Importer.ReadString(reader);
                }

            //System.Diagnostics.Debugger.Launch();

            
            

            // Read the object table
            reader.BaseStream.Seek(objectTableOffset, SeekOrigin.Begin);
            if (objectCount > 0)
                output.indexMap = new int[objectCount];

                for (int index = 0; index < objectCount; index++)
                {
                    // The first int is an index into the name table, the next two ints are the object header
                    string type = names[reader.ReadInt32() - 1];
                    long length = (long)reader.ReadInt32();

                    // Load a new object of the correct type into this content
                    long startOffset = reader.BaseStream.Position;
                    switch (type.ToLower())
                    {
                        case "brush":
                            Brush newBrush = new Brush(reader, names);
                            output.Brushes.Add(newBrush);
                           
                            break;

                        case "entity":
                            output.Entities.Add(new Entity(reader, names));
                            break;

                        case "group":
                            output.Groups.Add(new Group(reader));
                            break;

                        case "material":
                            output.Materials.Add(new Material(reader, names));
                            break;

                        case "visgroup":
                            output.VisibilityGroups.Add(new VisibilityGroup(reader, names));
                            break;

                        case "lightmap" :
                            Texture2DContent lightmap = readLightmaps(reader, names, output.Lightmaps.Count);
                            output.Lightmaps.Add(lightmap);
                            output.indexMap[index + 1] = output.Lightmaps.Count-1;
                            break;

                        case "meshreference" :
                            output.MeshReferences.Add(new MeshReference(reader, names, index+1));
                            output.indexMap[index + 1] = output.MeshReferences.Count-1;
                            break;

                        case "mesh" :
                            output.Meshes.Add(new Mesh(reader, names, output));
                            break;

                        case "terrain":
                            output.Terrains.Add(new Terrain(reader, names));
                            break;

                        default:
                            // This type is unsupported, so skip it
                            context.Logger.LogMessage("Skipped unsupported type" + type + length.ToString(" %d"));
                            reader.BaseStream.Seek(length, SeekOrigin.Current);
                            break;
                    }

                    // Sanity check - did we read the correct byte count?
                    long bytesRead = reader.BaseStream.Position - startOffset;
                    if (bytesRead != length)
                        throw new InvalidContentException(string.Format("Reading a '{0}' type consumed {1} bytes instead of the expected {2} bytes.", type, bytesRead, length));
                }

            // Set the lightmap point to index into the lightmap array 
            foreach (Brush brush in output.Brushes)
            {
                foreach (BrushFace bf in brush.AllFaces)
                {
                    if (bf.LightmapIndex == null) bf.LightmapIndex = -1;
                    else bf.LightmapIndex = output.indexMap[(int) bf.LightmapIndex];
                }
            }
            return output;
        }

     

        public static Texture2DContent readLightmaps(BinaryReader reader, string[] names,int count)
        {
            Byte flags;
            int resolution;
            int format;

            //System.Diagnostics.Debugger.Launch();

            // Read the material flags
            flags = (Byte)reader.ReadByte();

            // This object is just two strings, really
            resolution = reader.ReadByte();

            format = reader.ReadInt32();

            int lmsize = 2;
            for (int i = 0; i < resolution - 1; i++) lmsize *= 2;

            byte[] arrayData = new byte[lmsize * lmsize * 4];

            //System.Diagnostics.Debugger.Launch();

            for (int i = 0; i < lmsize; i++)
            {
                for (int j = 0; j < lmsize; j++)
                {
                    arrayData[((i * lmsize) + j) * 4] = reader.ReadByte();
                    arrayData[(((i * lmsize) + j) * 4) + 1] = reader.ReadByte();
                    arrayData[(((i * lmsize) + j) * 4) + 2] = reader.ReadByte();
                    arrayData[(((i * lmsize) + j) * 4) + 3] = 0xff;

                }
            }
#if CHECKERBOARDLIGHTMAPS
            
            for (int i = 0; i < lmsize; i++)
            {
                for (int j = 0; j < lmsize; j++)
                {
                    int modx = j / 4;
                    int mody = i / 4;
                    if ((modx & 1) == (mody & 1))
                    {
                        arrayData[((i * lmsize) + j) * 4] = 0x00;
                        arrayData[((i * lmsize) + j) * 4 + 1] = 0x0;
                        arrayData[((i * lmsize) + j) * 4 + 2] = 0x0;
                        arrayData[((i * lmsize) + j) * 4 + 3] = 0xff;

                        switch (count % 3)
                        {
                            case 0: arrayData[((i * lmsize) + j) * 4] = 0xff; break;
                            case 1: arrayData[((i * lmsize) + j) * 4 + 1] = 0xff; break;
                            case 2: arrayData[((i * lmsize) + j) * 4 + 2] = 0xff; break;
                        }
                    }
                    else
                    {
                        arrayData[((i * lmsize) + j) * 4] = 0x00;
                        arrayData[((i * lmsize) + j) * 4 + 1] = 0x00;
                        arrayData[((i * lmsize) + j) * 4 + 2] = 0x00;
                        arrayData[((i * lmsize) + j) * 4 + 3] = 0xff;
                    }
                    
                }
            }
#endif
            
            
            

            // Load the sprite texture into memory.
            BitmapContent bitMap = new PixelBitmapContent<Color>(lmsize, lmsize);
            bitMap.SetPixelData(arrayData);

            Texture2DContent textMap = new Texture2DContent();
            textMap.Mipmaps.Add(bitMap);

            return (textMap);

        }
        #endregion
    }
}
